Skip to content

fix(Tabs): only force-mount TabsContent during SSR#2477

Open
dfidler wants to merge 1 commit intounovue:v2from
dfidler:fix/2476-tabscontent-force-mount-spa
Open

fix(Tabs): only force-mount TabsContent during SSR#2477
dfidler wants to merge 1 commit intounovue:v2from
dfidler:fix/2476-tabscontent-force-mount-spa

Conversation

@dfidler
Copy link
Copy Markdown

@dfidler dfidler commented Feb 27, 2026

Fixes #2476

The Presence component inside TabsContent has force-mount hardcoded, keeping all inactive tab panels in the DOM. You can see this either by inspecting the DOM or when CSS display property (e.g. display:flex) is set on TabsContent, as it overrides the hidden attribute's display:none.

This change conditionally applies force-mount only during SSR (when document is undefined) or when the user explicitly passes the forceMount prop, using the existing isBrowser utility.

🔗 Linked issue

#2476

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Detects if running in SSR/SPA; respects the desire to hydrate all TabsContent in SSR but also respects documented behaviour for SPA

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • Bug Fixes
    • Improved server-side rendering compatibility for tab components. Fixed content mounting behavior to properly handle both server-side and client-side rendering. Tabs now correctly mount and display content in server environments while maintaining user-defined mounting controls in the browser, ensuring consistent rendering across all scenarios.

Fixes unovue#2476

The Presence component inside TabsContent has force-mount hardcoded,
keeping all inactive tab panels in the DOM. This causes inactive tabs
to be visible when any CSS display property (e.g. display:flex) is set
on TabsContent, as it overrides the hidden attribute's display:none.

This change conditionally applies force-mount only during SSR (when
document is undefined) or when the user explicitly passes the forceMount
prop, using the existing isBrowser utility.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78e762e and e734108.

📒 Files selected for processing (1)
  • packages/core/src/Tabs/TabsContent.vue

📝 Walkthrough

Walkthrough

The TabsContent component now conditionally sets the force-mount attribute based on the runtime environment. It imports isBrowser and applies dynamic binding :force-mount="!isBrowser || forceMount", ensuring SSR always mounts content while browser-based SPAs respect the explicit forceMount prop setting.

Changes

Cohort / File(s) Summary
TabsContent SSR/SPA Mounting Logic
packages/core/src/Tabs/TabsContent.vue
Added isBrowser import and replaced hardcoded force-mount with conditional binding to distinguish between SSR (always mount) and browser (respect prop value) contexts.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Possibly related PRs

  • unovue/reka-ui#2390: Modifies TabsContent.vue's mounting lifecycle behavior alongside other mount/unmount register logic changes.

Poem

🐰 A toggle so clever, so smart and so true,
Where SSR says "mount!" and the browser says "don't,"
One environment knows, the other just won't—
Now both get their way, hop hop, dreams come true! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: making force-mount conditional based on SSR detection rather than unconditionally forcing it.
Linked Issues check ✅ Passed The PR implementation matches issue #2476's requirements exactly: it uses isBrowser to detect SSR and sets force-mount to '!isBrowser || forceMount' as specified.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the TabsContent force-mount issue; no unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dfidler
Copy link
Copy Markdown
Author

dfidler commented Feb 27, 2026

Note: This is a code-fix only; I have not updated documentation though in retrospect, 9526356 probably should have documented that SSR forces forceMount = true

I can update the docs in my PR if you want or I can leave the wording of that up to the maintainers. Happy to take your guidance.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 2, 2026

Open in StackBlitz

npm i https://pkg.pr.new/reka-ui@2477

commit: 7d1be4a

@zernonia
Copy link
Copy Markdown
Member

zernonia commented Mar 9, 2026

Nice fix @dfidler ! A quick comment:

  • Potential behavior change for SPA users: Previously, inactive tab content stayed in the DOM (just hidden via hidden attribute). With this change, inactive tabs will unmount in the browser unless forceMount is set. This is arguably correct behavior, but might be (maybe minor) breaking change for users relying on DOM persistence (e.g. form state).

@dfidler
Copy link
Copy Markdown
Author

dfidler commented Mar 10, 2026

Previously, inactive tab content stayed in the DOM (just hidden via hidden attribute). With this change, inactive tabs will unmount in the browser unless forceMount is set. This is arguably correct behavior, but might be (maybe minor) breaking change for users relying on DOM persistence (e.g. form state).

Indeed. But that's the documented behaviour. It's the whole point of running tab content through Presence and it's why forceMount exists.

Though now that I think on it should presence ever unmount something in the SSR world? Was 9526356 a red herring and forceMount should just be overridden in the SSR world entirely?

I honestly don't know as I don't live in that world.

But then that turns a really small change into a huge change/risk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: TabsContent hard-codes force-mount with presence

2 participants